<?php

namespace App\Services;

use Illuminate\Support\Facades\Log;

class BtcNode{

    protected $rpcData;
    protected $url;
    protected $userName;
    protected $password;
    public $commission;
    protected $collectionAddress;
    public function __construct(){
        $this->rpcData = ["id"=>0,"jsonrpc"=>"2.0"];
        $this->url = config('user.omni_url','16.162.109.55:18332');
        $this->userName = 'xujunjie';
        $this->password = 'xujunjie123456';
        $this->centerAddress = '2Mt5VMV5HF3TiGExcUHuJQnXaM8aparJtje';
        $this->commission = 0.0001;
        $this->collectionAddress = '2N3ynv8bDvigAACKqRChXxr5RRzV5P14665';
    }

    /**
     * 创建地址
     */
    public function createAddress($accountName){
        $param[] = $accountName;
        $this->rpcData['method'] = "getnewaddress";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);

        if(!empty($result['result'])){
            return $result['result'];
        }else{
            Log::info("erc20创建地址失败");
            return false;
        }
    }

    /**
     * 验证地址有效性
     */
    public function validateAddress($address){
        $param[] = $address;
        $this->rpcData['method'] = "validateaddress";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result']['isvalid'];
        }else{
            return false;
        }
    }

    /**
     * 获取最新块
     */
    public function getblockcount(){
        $param = [];
        $this->rpcData['params'] = $param;
        $this->rpcData['method'] = "getblockcount";
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            return false;
        }
    }

    /**
     * 转账提现
     */
    public function withdraw($toAddress,$number,$propertyId = NULL){
        $fromAddress = $this->centerAddress;
        return $this->keepChange($fromAddress,$toAddress,$number,$propertyId);

    }

    public function collection($fromAddress,$number,$propertyId = NULL){
        $toAddress = $this->collectionAddress;
        return $this->keepChange($fromAddress,$toAddress,$number,$propertyId);
    }

    public function sendTransaction($fromAddress,$toAddress,$number,$propertyId){
        if(!empty($propertyId)){//omni
            $propertyId = (int)$propertyId;
            $param[] = $fromAddress;
            $param[] = $toAddress;
            $param[] = $propertyId;
            $param[] = (string)$number;
            $this->rpcData['method'] = "omni_send";
            $this->rpcData['params'] = $param;
            $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        }else{
            $param[] = $toAddress;
            $param[] = $number;
            $this->rpcData['method'] = "sendtoaddress";
            $this->rpcData['params'] = $param;
            $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        }
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            Log::info("omni转账失败:" . json_encode($param));
            return false;
        }
    }

    public function keepChange($fromAddress,$toAddress,$number,$propertyId = NULL){
        if(!empty($propertyId)){//omni
            if($unspentList = $this->listunspent($fromAddress,0.0006)){
                if(($tradeStr = $this->createrawtransaction($unspentList)) && ($opreturnStr = $this->omni_createpayload_simplesend($propertyId,$number))){
                    if($oneTradeStr = $this->omni_createrawtx_opreturn($tradeStr,$opreturnStr)){
                        if($twoTradeStr = $this->omni_createrawtx_reference($oneTradeStr,$toAddress)){
                            if($threeTradeStr = $this->omni_createrawtx_change($unspentList,$twoTradeStr,$fromAddress,0.0006)){
                                if($signData = $this->signrawtransactionwithwallet($threeTradeStr)){
                                    if($tradeId = $this->sendrawtransaction($signData['hex'])) {
                                        return $tradeId;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }else{
            if($unspentList = $this->listunspent($fromAddress,$number+0.0001)){
                if($tradeStr = $this->createrawtransaction($unspentList,$fromAddress,$toAddress,$number)){
                    if($signData = $this->signrawtransactionwithwallet($tradeStr,$unspentList)){
                        if($tradeId = $this->sendrawtransaction($signData['hex'])) {
                            return $tradeId;
                        }
                    }
                }
            }
        }
        Log::info("omni转账失败");
        return false;
    }

    public function listunspent($address,$number){
        $param = [1,99999999,[$address]];
        $this->rpcData['method'] = "listunspent";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            $unspentList = [];
            foreach($result['result'] as $value){
                if($value['amount'] > $number){
                    $number = 0;
                    $unspentList[] = $value;
                    break;
                }else{
                    $number = round($number - $value['amount'],8);
                    $unspentList[] = $value;
                }
            }
            if($number>0){
                Log::error($address . '地址余额不足');
                return false;
            }
            return $unspentList;
        }else{
            Log::error($address . '地址余额不足');
            return false;
        }
    }

    public function createrawtransaction($unspentList,$fromAddress = NULL,$toAddress = NULL,$number = NULL){
        $amountArr = array_column($unspentList,'amount');
        $total = array_sum($amountArr);
        $outArr = [];
        foreach($unspentList as $value){
            $row['txid'] = $value['txid'];
            $row['vout'] = $value['vout'];
            $outArr[] = $row;
        }
        if(empty($fromAddress) && empty($toAddress) && empty($number)){
            $param = [$outArr,[]];
        }else{
            $param = [$outArr,[$toAddress=>$number,$fromAddress=>round($total-$number-0.0001,6)]];
        }
        $this->rpcData['method'] = "createrawtransaction";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            Log::error('创建裸交易失败');
            return false;
        }
    }

    public function signrawtransactionwithwallet($tradeStr,$unspentList = NULL){
        if($unspentList == NULL){
            $param = [$tradeStr,[]];
        }else{
            $outArr = [];
            foreach($unspentList as $value){
                $row['txid'] = $value['txid'];
                $row['vout'] = $value['vout'];
                $row['redeemScript'] = $value['redeemScript'];
                $row['scriptPubKey'] = $value['scriptPubKey'];
                $row['amount'] = $value['amount'];
                $outArr[] = $row;
            }
            $param = [$tradeStr,$outArr];
        }
        $this->rpcData['method'] = "signrawtransactionwithwallet";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            Log::error('签名交易失败');
            return false;
        }
    }

    public function sendrawtransaction($signStr){
        $param = [$signStr];
        $this->rpcData['method'] = "sendrawtransaction";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            Log::error('广播交易失败');
            return false;
        }
    }

    public function omni_createpayload_simplesend($propertyId,$number){
        $param = [(int)$propertyId,(string)$number];
        $this->rpcData['method'] = "omni_createpayload_simplesend";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            Log::error('构建代币交易失败');
            return false;
        }
    }

    public function omni_createrawtx_opreturn($tradeStr,$opreturnStr){
        $param = [$tradeStr,$opreturnStr];
        $this->rpcData['method'] = "omni_createrawtx_opreturn";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            Log::error('附加代币交易失败');
            return false;
        }
    }

    public function omni_createrawtx_reference($oneTradeStr,$toAddress){
        $param = [$oneTradeStr,$toAddress];
        $this->rpcData['method'] = "omni_createrawtx_reference";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            Log::error('附加地址失败');
            return false;
        }
    }

    public function omni_createrawtx_change($unspentList,$twoTradeStr,$fromAddress,$commision){

        $outArr = [];
        foreach($unspentList as $value){
            $row['txid'] = $value['txid'];
            $row['vout'] = $value['vout'];
            $row['redeemScript'] = $value['redeemScript'];
            $row['scriptPubKey'] = $value['scriptPubKey'];
            $row['value'] = $value['amount'];
            $outArr[] = $row;
        }

        $param = [$twoTradeStr,$outArr,$fromAddress,$commision];
        $this->rpcData['method'] = "omni_createrawtx_change";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            Log::error('设置找零地址和矿工费失败');
            return false;
        }
    }

    /**
     * 查询交易信息
     */
    public function getTradeInfo($tradeId){
        $data = $this->getOmniTradeInfo($tradeId);
        if(!empty($data)){
            return $data;
        }
        $param = [$tradeId];
        $this->rpcData['method'] = "gettransaction";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);

        $data = [];
        $data['details'] = $result['result']['details'];
        $data['trade_id'] = $tradeId;
        $data['confirmations'] = $result['result']['confirmations'];
        return $data;
    }

    /**
     * 查询omni代币交易信息
     */
    public function getOmniTradeInfo($tradeId){
        $param = [$tradeId];
        $this->rpcData['method'] = "omni_gettransaction";
        $this->rpcData['params'] = $param;
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if($result['error'] != null){
            return false;
        }
        $data = [];
        $data['sendAddress'] = $result['result']['sendingaddress'];
        $data['receiveAddress'] = isset($result['result']['referenceaddress']) ? $result['result']['referenceaddress'] : '';
        $data['amount'] = $result['result']['amount'];
        $data['trade_id'] = $tradeId;
        $data['confirmations'] = $result['result']['confirmations'];
        $data['property_id'] = $result['result']['propertyid'];
        return $data;
    }

    /**
     * 获取块的hash
     */
    public function getblockhash($number){
        $param = [$number];
        $this->rpcData['params'] = $param;
        $this->rpcData['method'] = "getblockhash";
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            return false;
        }
    }

    /**
     * 根据块hash获取块交易
     */
    public function getblock($hash){
        $param = [$hash];
        $this->rpcData['params'] = $param;
        $this->rpcData['method'] = "getblock";
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            return false;
        }
    }

    /**
     * 获取指定区块后发生的钱包交易
     */
    public function listsinceblock($blockHash){
        $param = [$blockHash,1,true];
        $this->rpcData['params'] = $param;
        $this->rpcData['method'] = "listsinceblock";
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            return false;
        }
    }

    /**
     * 获取指定区块omni代币交易
     */
    public function listBlockTransactions($blockNumber){
        $param = [$blockNumber];
        $this->rpcData['params'] = $param;
        $this->rpcData['method'] = "omni_listblocktransactions";
        $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
        if(!empty($result['result'])){
            return $result['result'];
        }else{
            return false;
        }
    }

    public function getBalance($address,$propertyId){
        $propertyId = (int) $propertyId;
        if(!empty($propertyId)){
            $param = [$address,$propertyId];
            $this->rpcData['params'] = $param;
            $this->rpcData['method'] = "omni_getbalance";
            $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
            if(!empty($result['result'])){
                return $result['result']['balance'];
            }else{
                return false;
            }
        }else{
            $param = [1,99999999,[$address]];
            $this->rpcData['method'] = "listunspent";
            $this->rpcData['params'] = $param;
            $result = http_chain($this->url,$this->rpcData,$this->userName,$this->password);
            if(!empty($result['result'])){
                $amountArr = array_column($result['result'],'amount');
                $amountNum = array_sum($amountArr);

                if($amountNum<0){
                    Log::error($address . '地址余额不足');
                    return false;
                }else{
                    return $amountNum;
                }
            }else{
                Log::error($address . '地址余额不足');
                return false;
            }
        }

    }
}
